#include "audio_render.h"

#include <map>
#include <thread>

#include "dpl_error.h"

namespace dpl {


const std::map<int, int> kAudioFormatCvtMap ={
    {AV_SAMPLE_FMT_U8,   AUDIO_U8},
    {AV_SAMPLE_FMT_S16,  AUDIO_S16},    
    {AV_SAMPLE_FMT_S32,  AUDIO_S32},   
    {AV_SAMPLE_FMT_FLT,  AUDIO_F32},
    {AV_SAMPLE_FMT_DBL,  0},
    {AV_SAMPLE_FMT_U8P,  0},
    {AV_SAMPLE_FMT_S16P, 0},
    {AV_SAMPLE_FMT_S32P, 0},
    {AV_SAMPLE_FMT_FLTP, 0},
    {AV_SAMPLE_FMT_DBLP, 0},
    {AV_SAMPLE_FMT_S64,  0},
    {AV_SAMPLE_FMT_S64P, 0},
    {AV_SAMPLE_FMT_NB,   0},
    {AV_SAMPLE_FMT_NONE, 0},
    };


void FillAudioCallback(void* udata, Uint8* stream, int len)
{

    AudioRender* render = static_cast<AudioRender*>(udata);
    render->FillAudio(stream, len);
}

AudioRender::AudioRender() paused_(1)
{

}

AudioRender::~AudioRender()
{
    SDL_CloseAudio();
}

int AudioRender::OpenAudio(const AVCodecParameters &param)
{
    int sample_format = kAudioFormatCvtMap[param.format];

    if(sample_format == 0) {
        dpl_error("unsupport audio device sample rate");
        return ERROR_INVALID_ARGUMENT;
    }

    audio_spec_.freq = param.sample_rate;
    audio_spec_.format = sample_format;
    audio_spec_.channels = param.channels;
    audio_spec_.samples =  param.channels * param.sample_rate * 10 / 1000; // 10ms 
    audio_spec_.callback = FillAudioCallback;
    audio_spec_.userdata = this;

    //根据参数，打开音频设备
    if (SDL_OpenAudio(&audio_spec_, NULL) < 0) {
        dpl_error("open audio device failed %s", SDL_GetError());
        return ERROR_FAIL;
    }

    int buffer_size = param.channels * param.sample_rate * SDL_AUDIO_BITSIZE(audio_spec_.format) / 8 * 10; // 10s cache
    buffer_.resize(buffer_size);

    return ERROR_SUCCESS;
}

int AudioRender::FillAudio(uint8_t* stream int len)
{
    SDL_memset(stream, 0, len);
    // SDL_MixAudio(stream, stream, len, SDL_MIX_MAXVOLUME);
    return buffer_.pop(stream, len);
}

int AudioRender::Enqueue(AVFrame* frame, std::chrono::milliseconds wait_dur)
{
    int nwrite = 0;
    std::chrono::milliseconds try_interval(10);

    nwrite += buffer_.push(frame->data[0] + nwrite, frame->linesize[0] - nwrite);
    wait_dur -= try_interval;

    while (nwrite < frame->linesize[0] && (wait_dur > 0)) {
        std::this_thread::sleep_for(try_interval);

        nwrite += buffer_.push(frame->data[0] + nwrite, frame->linesize[0] - nwrite);
    }

    av_frame_free(frame);
    
    return nwrite;
}

int AudioRender::Clear()
{
    buffer_.clear();
    return ERROR_SUCCESS;
}

int AudioRender::StartRender()
{
    paused_ = 0;
    SDL_PauseAudio(0);
    return ERROR_SUCCESS;
}

int AudioRender::StopRender()
{
    paused_ = 1;
    SDL_PauseAudio(1);
    return ERROR_SUCCESS;
}

int AudioRender::TogglePause()
{
    paused_ = (paused_ + 1) % 2;
    SDL_PauseAudio(paused_);
    return ERROR_SUCCESS;
}

}